/*
Copyright (C) 2022-2024 Inspur Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/
package service

import (
	"crypto/rand"
	"did-service/bean/entity"
	"did-service/bean/model"
	"did-service/bean/param"
	"did-service/common/constant"
	"did-service/common/crypto/gm"
	"did-service/common/uuid"
	"did-service/config"
	"did-service/plugins/apicheck"
	param2 "did-service/plugins/apicheck/param"
	"encoding/hex"
	"time"
)

func (srv *DidService) CreateApiKey(walletId string) (*model.CreateApiKeyResult, error) {
	did, err := srv.GetOldestDidInWallet(walletId)
	if err != nil {
		log.Errorf("create api key, get oldest did error: %v", err)
		return nil, err
	}

	didKey, err := srv.GetDocumentKeyByDid(did)
	if err != nil {
		log.Errorf("create api key, get did document error: %v", err)
		return nil, err
	}
	prvKeyHex, pubKeyHex := didKey.PrvKey, didKey.PubKey
	prvKey := gm.GetPrvKeyBase64(prvKeyHex)
	pubKey := gm.GetPubKeyBase64(pubKeyHex)
	apiKey := uuid.Generate()
	buffer := make([]byte, 64)
	rand.Read(buffer)
	apiSecret := hex.EncodeToString(buffer)
	now := time.Now()
	didAipKey := &entity.DidApiKey{
		Id:          uuid.Generate(),
		WalletId:    walletId,
		ApiKey:      apiKey,
		ApiSecret:   apiSecret,
		PrvKey:      prvKeyHex,
		PubKey:      pubKeyHex,
		Status:      constant.Status1,
		CreatedTime: now,
		UpdatedTime: now,
	}
	_, err = srv.engine.InsertOne(didAipKey)
	if err != nil {
		log.Errorf("create api key, insert api key error: %v", err)
		return nil, err
	}

	return &model.CreateApiKeyResult{
		WalletId:  walletId,
		Did:       did,
		ApiKey:    apiKey,
		ApiSecret: apiSecret,
		PrvKey:    prvKey,
		PubKey:    pubKey,
	}, nil
}

func (srv *DidService) GetOldestDidInWallet(wallet string) (string, error) {
	var did string
	_, err := srv.engine.Table("did_document").
		Where("wallet_id = ? and status = ?", wallet, "1").OrderBy("created_time").
		Cols("did").Get(&did)
	if err != nil {
		return "", err
	}
	return did, nil
}

func (srv *DidService) CheckApi(apiKey, signature string, p interface{}) (bool, string, error) {
	var didApiKey entity.DidApiKey
	ok, err := srv.engine.Where("api_key = ? and status = ?", apiKey, constant.Status1).Get(&didApiKey)
	if err != nil {
		log.Errorf("get did api key from db error: %v", err)
		return false, "", err
	}
	if !ok {
		log.Error("api key invalid")
		return false, "", nil
	}
	apiSecret := didApiKey.ApiSecret
	pubKey := didApiKey.PubKey
	payload := apicheck.Get().ExtractPayload(p)
	result := apicheck.Get().CheckSignature(&param2.ApiCheckParam{
		Payload:   payload,
		ApiKey:    apiKey,
		ApiSecret: apiSecret,
		Signature: signature,
		PubKey:    pubKey,
	})
	return result, didApiKey.WalletId, err
}

func (srv *DidService) ApplyVcWithoutDid(p *param.ApplyVcWithoutDidParam) (*model.ApplyVcWithoutDidResult, error) {
	var didApiKey entity.DidApiKey
	srv.engine.Where("api_key = ? and status = ?", p.ApiKey, constant.Status1).Get(&didApiKey)

	didDoc, err := srv.CreateDid(didApiKey.WalletId)
	if err != nil {
		return nil, err
	}

	para := param.ApplyCredentialParam{
		IssuerId: p.IssuerId,
		CptId:    p.CptId,
		Type:     "00",
		CredentialSubject: model.CredentialSubject{
			Id:     didDoc.Did,
			Claims: p.Claims,
		},
	}

	//检查发证方状态
	issuerDid, _, err := srv.ValidateIssuerStatus(para.IssuerId)
	if err != nil {
		return nil, err
	}
	//验证发证方id和cptId是否匹配
	err = srv.ValidateIssuerIdAndCptId(para.IssuerId, para.CptId)
	if err != nil {
		log.Errorf("error: %v", err)
		return nil, err
	}
	//验证CPT属性
	err = srv.ValidateCptClaim(p.CptId, para.CredentialSubject)
	if err != nil {
		return nil, err
	}
	//校验credentialSubject
	err = srv.ValidateCredSubject(didDoc.WalletId, para.CredentialSubject)
	if err != nil {
		return nil, err
	}

	credId := uuid.Generate()
	//生成可验证凭证
	if err = srv.GenerateCredential(credId, p.IssuerId, issuerDid, para, didDoc.WalletId); err != nil {
		log.Errorf("err: %v", err)
		return nil, err
	}

	vc, err := srv.QueryCredentialDetail(didDoc.WalletId, credId)
	if err != nil {
		log.Errorf("query credential detail error: %v", err)
		return nil, err
	}

	keys, err := srv.ListKeyByDid(didDoc.Did)
	if err != nil {
		log.Errorf("get keys by did error: %v", err)
		return nil, err
	}
	keyPairs := make([]model.KeyPair, 0)
	for _, d := range keys {
		keyPair := model.KeyPair{}
		keyPair.Id = d.KeyId
		keyPair.Type = constant.DefaultKeyAlgo
		keyPair.PublicKeyHex = d.PubKey
		keyPair.PrivateKeyHex = d.PrvKey
		keyPairs = append(keyPairs, keyPair)
	}

	services := make([]model.Service, 0)
	service := model.Service{}
	service.Id = didDoc.Did + constant.DefaultServiceResolveSubfix
	service.Type = constant.DefaultServiceResolveType
	service.ServiceEndpoint = config.Get("did.endpoint", constant.DefaultServiceResolveEndpoint) + constant.DidResolveUrlSuffix
	services = append(services, service)

	proof := model.Proof{
		Type:           constant.DefaultKeyAlgo,
		Creator:        didDoc.ProofCreator,
		SignatureValue: didDoc.ProofSignature,
	}

	doc := &model.Document{
		Context:        constant.DefaultContext,
		Id:             didDoc.Did,
		Version:        didDoc.Version,
		Created:        didDoc.CreatedTime.Format(constant.TimeLayout),
		Updated:        didDoc.UpdatedTime.Format(constant.TimeLayout),
		KeyPairs:       keyPairs,
		Authentication: []string{didDoc.AuthPubKey},
		Recovery:       []string{didDoc.RecoveryPubKey},
		Service:        services,
		Proof:          proof,
	}
	return &model.ApplyVcWithoutDidResult{
		VcId:     credId,
		Did:      didDoc.Did,
		Document: doc,
		Vc:       vc.Credential,
	}, nil
}
